 
/* 
 * Copyright (C) Ignacio Lucas
 */
 
/*
 *  Implementacion de las aplicaciones de prueba para la tesis (aplicacion bancaria y balanceo de carga).
 *  El manejo de HTTP esta implementado de una forma similar y simplificada del concepto de Servlet en Java.
 *
 *  Ignacio Lucas <ignacio.lucas@gmail.com>
 *
 */
#include "http_servlet.h"
#include "../socket_utils.h"
#include "accounts_service.h"
#include "../utils/bstrlib.h"
#include "../utils/bstraux.h"
#include "http_servlet_dummy_text.h"
#include "../user_util.h"
#include "../priority_queue.h"

// para la pagina de balanceo de carga. Random de 4 servidores donde se derivan los requerimientos.
static int random_port_load_balancer=0;

// devuelve un random entre 8082 y 8085 para ser utilizados como puertos TCP donde se derivan los requerimientos.
// utilizado en la aplicacion de balanceo de carga.
static inline int get_random_port_load_balancer()
{
	random_port_load_balancer=((random_port_load_balancer + 1) % 4);
	return 8082 + random_port_load_balancer;
}

static hashmap_t request_path_mapping = NULL;

static int random_port=0;

static int http_servlet_destroy_request(http_request_t request);
static int http_servlet_destroy_response(http_response_t response);
static void http_servlet_add_stylesheet(bstring html);

// puerto random TCP entre 8082 y 8084 para derivar los requerimientos de movimientos de cuentas de una cuenta particular.
static inline int get_random_port()
{
	random_port=((random_port + 1) % 3);
	return 8082 + random_port;
}

int http_servlet_destroy()
{
	http_destroy();
	if (request_path_mapping != NULL)
	{
		hashmap_delete(request_path_mapping);
	}
	return 0;
}

// inicializa la respuesta para devolver al cliente, agregando encabezados comunes a todas las respuestas.
static int http_servlet_init_response(http_response_t response, http_request_t request)
{
	//char *host_value;
	http_response_set_status_code(response, 200);
	//if (http_request_get_header(request, "Host", &host_value) == 0)
	//{
	http_response_add_header(response, "Host", "tesis");
	http_response_add_header(response, "Content-Type", "text/html; charset=ISO-8859-1");
	http_response_add_header(response, "Connection", "close");
	http_response_add_header(response, "Pragma", "no-cache");
	http_response_add_header(response, "Cache-control", "no-cache");
	//}
	return 0;	
}

// deriva en la funcion correspondiente que atiende la URL solicitada en request.
static int http_servlet_service(server_worker_job_t worker_job, http_request_t request, http_response_t response)
{
	int ret=0;
	
	int (*http_servlet_func)(server_worker_job_t, http_request_t, http_response_t) = NULL;
	
	// inicializa la response.
	http_servlet_init_response(response, request);
	
	// Ubico la funcion que procesara este requerimiento.
	hashmap_entry_by_key(request_path_mapping, http_request_get_request_path(request), (void **)(&http_servlet_func));
	
	if (http_servlet_func == NULL)
	{
		// NOT FOUND!
		http_response_set_status_code(response, 404);
		http_response_set_body(response, "NOT FOUND!", 10, 0);
	}
	else
	{
		// ejecuta la funcion que atiende al request.
		ret=http_servlet_func(worker_job, request, response);
	}
	return ret;
}


int http_servlet_implementation(server_worker_job_t worker_job,void *args)
{
	int ret=0, servlet_ret=0;
	http_request_t request = NULL;
	http_response_t response = NULL;
	socket_t client_socket;
	
	client_socket = (socket_t)server_worker_job_get_args(worker_job);
	
	// parsea el requerimiento del cliente.
	request = http_request_parse(client_socket);
	if (request == NULL)
	{
		logger_app_info("%s","[SERVLET] no se pudo parsear request!");
		socket_destroy(client_socket);
		return 1;
	}
	
	// inicializa el objeto que representa la respuesta a entregar al cliente.
	response = http_response_create(request);
	if (response == NULL)
	{
		logger_app_info("%s","[SERVLET] no se pudo crear response!");
		http_request_destroy(request, 0);
		socket_destroy(client_socket);
		return 1;
	}

	// asumimos status 200 inicial
	http_response_set_status_code(response, 200);
	
	// derivamos en la funcion que procesa el requerimiento.
	servlet_ret=http_servlet_service(worker_job, request, response);
	
	// Serializo la respuesta en client_socket
	ret=http_response_serialize_and_send_on_socket(response, client_socket);
	
	// log con el resultado del procesamiento del requerimiento.
	logger_app_info("url: %s, http_response: %d, sent_bytes: %d. servlet_ret: %d", http_request_get_request_url(request), http_response_get_status_code(response), ret, servlet_ret);
	
	// liberamos recursos del request y response.
	http_request_destroy(request, 1);
	http_response_destroy(response, 0);
	return ret;	
}

// inicializacion de un request completo.
static http_request_t http_servlet_create_request(
										server_worker_job_t worker_job,
										char *hostname, int port, 
										char *request_path, enum http_method meth,
										unsigned short http_major, unsigned short http_minor)
{
	int error=0;
	socket_t socket = NULL;
	http_request_t newrequest = NULL;
	
	newrequest = http_request_create(meth, http_major, http_minor, request_path);
	if (newrequest == NULL)
	{
		logger_app_error("[http_servlet] no pudo crear request al servidor. host:'%s', port %d", hostname, port);
		return NULL;
	}
	// obligatorio para HTTP/1.1
	http_request_add_header(newrequest, "Host", hostname);
		
	socket=socket_tcp_create_and_connect(worker_job, hostname, port, &error);
	if (socket == NULL)
	{
		http_request_destroy(newrequest, 0);
		logger_app_error("[http_servlet] no pudo crear conexion al host:'%s', port %d. err=%d", hostname, port, error);
		return NULL;
	}
	
	http_request_set_socket(newrequest, socket);
	return newrequest;
}

// destruye un http_request.
static int http_servlet_destroy_request(http_request_t request)
{
	if (request == NULL) return 1;
	return http_request_destroy(request, 1);
}

// destruye un http_response.
static int http_servlet_destroy_response(http_response_t response)
{
	if (response == NULL) return 1;
	return http_response_destroy(response, 0);
}

// envia un http_request a un servidor y construye la respuesta HTTP acorde.
static http_response_t http_servlet_send_request_and_create_response(http_request_t request, server_worker_job_t worker_job)
{
	http_response_t response = NULL;
	socket_t socket = NULL;
	int ret = 0;
	
	// Enviar request.
	ret=http_request_serialize_and_send(request);
	if (ret != 0)
	{
		logger_app_error("%s","[http_servlet_send_request_and_create_response] no pudo enviar request al server.");
		return NULL;
	}
	socket=http_request_get_socket(request);
	response = http_response_parse(socket);
	if (response != NULL)
	{
		http_servlet_init_response(response, request);
	}
	return response;
}

// funcion que deriva en un servidor proveedor de informacion la obtencion de las cuentas de un cliente a partir de su identificador "id_cliente".
static cuenta *http_servlet_get_cuentas_cliente(server_worker_job_t worker_job, bstring html, char *id_cliente, int *count_cuentas)
{
	http_request_t newrequest = NULL;
	http_response_t newresponse = NULL;
	cuenta *cuentas=NULL;
	int response_status_code=200;
	char *accounts_buffer;
	int accounts_buffer_len;

	*count_cuentas=0;
	newrequest=http_servlet_create_request(worker_job, getenv("TESIS_ACCOUNTS_IP_SERVICE"),8080,"/service/cuentas.jsp", (enum http_method)HTTP_GET,1,1);
	if (newrequest == NULL)
	{
		bformata(html, "<h1>Error inesperado (cod: HTTP_CREATE_REQUEST)</h1>");
		return NULL;
	}

	// agregado de parametro de id cliente al subrequest.
	http_request_add_parameter(newrequest, "id_cliente", id_cliente);

	// pedimos las cuentas del cliente.
	newresponse=http_servlet_send_request_and_create_response(newrequest, worker_job);
	if (newresponse == NULL)
	{
		http_servlet_destroy_request(newrequest);
		http_servlet_destroy_response(newresponse);
		bformata(html, "<h1>Error inesperado (cod: HTTP_CREATE_RESPONSE)</h1>");
		logger_app_error("%s","[http_servlet_cuentas] error ejecutando cuentas request.");
		return NULL;
	}
	
	response_status_code=http_response_get_status_code(newresponse);
	if (response_status_code != 200)
	{
		http_servlet_destroy_request(newrequest);
		http_servlet_destroy_response(newresponse);
		bformata(html, "<h1>Error inesperado (cod: HTTP_RESPONSE_%d)</h1>", response_status_code);
		return NULL;
	}
	http_response_get_body(newresponse, (const char **)&(accounts_buffer), &(accounts_buffer_len));

	// recuperamos las cuentas.
	cuentas=get_cuentas(accounts_buffer, count_cuentas);

	http_servlet_destroy_request(newrequest);
	http_servlet_destroy_response(newresponse);

	if (cuentas == NULL)
	{
		bformata(html, "<h1>Error inesperado (cod: GET_CUENTAS)</h1>");
		return NULL;
	}
	return cuentas;
}

// funcion que atiende el requermiento "/cuentas.jsp". Muestra las cuentas de un cliente.
static int http_servlet_cuentas(server_worker_job_t worker_job, http_request_t request, http_response_t response)
{
	bstring html=bfromcstr("\0");
	char *id_cliente;
	cuenta *cuentas=NULL;
	int count_cuentas=0, i=0;

	// sin memoria disponible.
	if ((html == NULL) || (balloc(html, 32767) != BSTR_OK))
	{
		http_response_set_body(response, "<html><head/><body><h1>Error inesperado (cod: HTML_BUFFER_CREATE)</h1></body></html>", strlen("<html><head/><body><h1>Error inesperado (cod: HTML_BUFFER_CREATE)</h1></body></html>"), 0);
		http_response_set_status_code(response, 500);
		return 0;
	}

	// construimos el html de respuesta.
	bformata(html, "<html lang=\"es\"><head><title>Cuentas</title>");
	http_servlet_add_stylesheet(html);
	bformata(html, "</HEAD><body>");

	id_cliente=http_request_get_parameter(request, "id_cliente");
	
	// el id de cliente no fue especificado.
	if (id_cliente == NULL)
	{
		bformata(html, "<h1>El cliente no fue especificado</h1></body></html>");
		http_response_set_body(response, bdata(html), blength(html), 1);
		return 0;
	}

	// recuperamos las cuentas del cliente.
	cuentas=http_servlet_get_cuentas_cliente(worker_job, html, id_cliente, &count_cuentas);

	if ((cuentas == NULL) || (count_cuentas == 0))
	{
		bformata(html, "</body></html>");
		http_response_set_body(response, bdata(html), blength(html), 1);
		http_response_set_status_code(response, 500);
		return 0;
	}

	bformata(html, "<p class=\"titulo\">Saldos de Cuentas</p><center><table><tr><td><table><tr>"
			 "<th height=\"17\">Tipo</th>"
			 "<th height=\"17\">N&uacute;mero</th>"
			 "<th height=\"17\">Saldo</th>"
			 "<th height=\"17\" colspan=\"2\">Operaci&oacute;n</th>"
			"</tr>");

	// agregamos todas las cuentas del cliente.
	for (i=0; i<count_cuentas; i++)
	{
			bformata(html, "<tr><td height=\"25\" class=\"textoCeldaAzul\">%s</td><td class=\"textoCeldaAzul\">%s</td><td class=\"textoCeldaAzul\">%s %ld</td><td class=\"textoCeldaVerde\"><a class=\"noLink\" href=\"movimientos_cuenta.jsp?cuenta=%s&sort=0&id_cliente=%s\">Movimientos ordenados por fecha</a></td><td class=\"textoCeldaVerde\"><a class=\"noLink\" href=\"movimientos_cuenta.jsp?cuenta=%s&sort=1&id_cliente=%s\">Movimientos ordenados por monto</a></td></tr>", get_tipo_cuenta(cuentas[i].tipo_cuenta), cuentas[i].cuenta, get_moneda_por_tipo_cuenta(cuentas[i].tipo_cuenta), cuentas[i].saldo, cuentas[i].cuenta, id_cliente, cuentas[i].cuenta, id_cliente);
	}
	bformata(html, "<tr><td colspan=\"3\">&nbsp;</td>"
			 "<td height=\"17\" class=\"textoCeldaVerde\"><a class=\"noLink\" href=\"movimientos_all_cuentas.jsp?id_cliente=%s&sort=0\">Consolidado ordenado por fecha</a></td>"
			 "<td height=\"17\" class=\"textoCeldaVerde\"><a class=\"noLink\" href=\"movimientos_all_cuentas.jsp?id_cliente=%s&sort=1\">Consolidado ordenado por monto</a></td>"
			"</tr></table></td></tr></table></center></body></html>", id_cliente, id_cliente);

	// Liberamos cuentas.
	cuentas_destroy(cuentas, count_cuentas);

	// agregamos el body a la respuesta.
	http_response_set_body(response, bdata(html), blength(html), 1);

	return 0;
}

// agrega los estilos CSS a las respuestas entregadas al cliente.
static void http_servlet_add_stylesheet(bstring html)
{
	bformata(html,"<style>"
		"BODY{"
			"font-family: Arial, Helvetica;"
			"font-size: 11px;"
			"color: #333333;"
			"background-color: #FFFFFF;"
			"padding-left: 20px;"
		"}"
		"TH"
		"{"
			"padding: 0px 10px;"
			"font-family: Arial, Helvetica;"
			"font-size:11px;"
			"font-weight:bold;"
			"color:#FFFFFF;"
			"background-color:#818181;"
		"}"
		"P"
		"{"
			"font-family: Arial, Helvetica;"
			"font-size: 11px;"
			"color: #333333;"
		"}"
		".titulo"
		"{"
			"font-family: Arial, Helvetica;"
			"font-size: 13px;"
			"font-weight: bold;"
			"color: #147ec4;"
		"}"
		".subtitulo"
		"{"
			"font-family: Arial, Helvetica;"
			"font-size: 11px;"
			"font-weight: bold;"
			"color: #555;"
		"}"
		".noLink"
		"{"
			"vertical-align:middle;"
			"text-decoration:none;"
		"}"
		".tituloCelda"
		"{"
			"font-family: Arial, Helvetica;"
			"font-size:11px;"
			"font-weight:bold;"
			"color:#FFFFFF;"
			"background-color:#818181;"
		"}"
		".tituloCeldaLeft"
		"{"
			"font-family: Arial, Helvetica;"
			"font-size:11px;"
			"font-weight:bold;"
			"text-align:left;"
			"color:#FFFFFF;"
			"background-color:#818181;"
		"}"
		".textoCeldaAzul"
		"{"
			"padding: 0px 10px;"
			"font-family: Arial, Helvetica;"
			"font-size:11px;"
			"color:#333333;"
			"text-align:center;"
			"background-color: #d7e9f5;"
		"}"
		".textoCeldaAzulLeft"
		"{"
			"padding: 0px 10px;"
			"font-family: Arial, Helvetica;"
			"font-size:11px;"
			"color:#333333;"
			"text-align:left;"
			"background-color: #d7e9f5;"
		"}"
		".textoCeldaVerdeLeft"
		"{"
			"padding: 0px 10px;"
			"font-family: Arial, Helvetica;"
			"font-size:11px;"
			"color:#333333;"
			"text-align:left;"
			"background-color: #d4e7e3;"
		"}"
		".textoCeldaVerdeRight"
		"{"
			"padding: 0px 10px;"
			"font-family: Arial, Helvetica;"
			"font-size:11px;"
			"color:#333333;"
			"text-align:right;"
			"background-color: #d4e7e3;"
		"}"
		".textoCeldaVerde"
		"{"
			"padding: 0px 10px;"
			"font-family: Arial, Helvetica;"
			"font-size:11px;"
			"color:#333333;"
			"text-align:center;"
			"background-color: #d4e7e3;"
		"}</style>");

}


// construye la porcion de HTML asociada a los movimientos de cuenta de un cliente.
// realiza un requerimiento al proveedor de informacion para recuperar los movimientos de la cuenta "str_cuenta" perteneciente
// al cliente cuyo id es "id_cliente".
static int http_servlet_get_movimientos_cuenta(server_worker_job_t worker_job, bstring html, char *id_cliente, char *str_cuenta, char *str_sort_criteria)
{
	http_request_t newrequest = NULL;
	http_response_t newresponse = NULL;
	int sort_criteria=0; // por default ordenamos los movimientos por fecha.
	int response_status_code=200;
	char *accounts_buffer;
	int accounts_buffer_len;
	cuenta *cuenta=NULL;
	int i=0;
	long debitos, creditos;

	// repartimos la carga de los movimientos de cuenta entre los puertos 8082 y 8083.
	int port = get_random_port();

	// el id de cliente no fue especificado.
	if (id_cliente == NULL)
	{
		bformata(html, "<h1>No fue especificado el ID de cliente</h1>");
		return -1;
	}

	// la cuenta no fue especificada.
	if (str_cuenta == NULL)
	{
		bformata(html, "<h1>No fue especificada la cuenta</h1>");
		return -1;
	}

	newrequest=http_servlet_create_request(worker_job, getenv("TESIS_ACCOUNTS_IP_SERVICE"),port,"/service/movimientos_cuenta.jsp", (enum http_method)HTTP_GET,1,1);
	if (newrequest == NULL)
	{
		bformata(html, "<h1>Error inesperado (cod: HTTP_CREATE_REQUEST)</h1>");
		return -1;
	}

	// agregado de parametro de id cliente al subrequest.
	http_request_add_parameter(newrequest, "cuenta", str_cuenta);

	// pedimos las cuentas del cliente.
	newresponse=http_servlet_send_request_and_create_response(newrequest, worker_job);
	if (newresponse == NULL)
	{
		http_servlet_destroy_request(newrequest);
		http_servlet_destroy_response(newresponse);
		bformata(html, "<h1>Error inesperado (cod: HTTP_CREATE_RESPONSE)</h1>");
		logger_app_error("%s","[http_servlet_cuentas] error ejecutando cuentas request.");
		return -1;
	}
	
	response_status_code=http_response_get_status_code(newresponse);
	if (response_status_code != 200)
	{
		http_servlet_destroy_request(newrequest);
		http_servlet_destroy_response(newresponse);
		bformata(html, "<h1>Error inesperado (cod: HTTP_RESPONSE_%d)</h1>", response_status_code);
		return -1;
	}
	http_response_get_body(newresponse, (const char **)&(accounts_buffer), &(accounts_buffer_len));

	// recuperamos los movimientos de cuenta.
	sort_criteria = (str_sort_criteria==NULL)?0:atoi(str_sort_criteria);

	cuenta=get_movimientos_de_cuenta(accounts_buffer, sort_criteria);

	// ya podemos liberar el subrequest/subresponse.
	http_servlet_destroy_request(newrequest);
	http_servlet_destroy_response(newresponse);

	if (cuenta == NULL)
	{
		bformata(html, "<h1>Error inesperado (cod: GET_CUENTAS)</h1>");
		return -1;
	}
	// construimos el html de respuesta.
	bformata(html, "<p class=\"titulo\">Movimientos de Cuenta %s %s</p><center><table><tr>"
			 "<th height=\"17\">Detalle</th>"
			 "<th height=\"17\">Monto</th>"
			 "<th height=\"17\">Fecha</th>"
			"</tr>", get_tipo_cuenta(cuenta->tipo_cuenta), cuenta->cuenta);

	// agregamos todas las cuentas del cliente.
	for (i=0; i<cuenta->movimientos.len; i++)
	{
			bformata(html, "<tr><td height=\"25\" class=\"textoCeldaAzul\">%s</td><td class=\"textoCeldaAzul\">%c %s %ld</td><td class=\"textoCeldaAzul\">%s</td></tr>", cuenta->movimientos.movimiento[i].descripcion, ((cuenta->movimientos.movimiento[i].debito_credito=='0')?'-':'+'), get_moneda_por_tipo_cuenta(cuenta->tipo_cuenta), cuenta->movimientos.movimiento[i].monto, cuenta->movimientos.movimiento[i].fecha_y_hora);
	}
	
	totalizar_cuenta(cuenta, &debitos, &creditos);

	bformata(html, "</table><br/><table><tr>"
			 "<th height=\"17\">Saldo Cuenta</th>"
			 "<th height=\"17\">Total d&eacute;bitos</th>"
			 "<th height=\"17\">Total cr&eacute;ditos</th>"
			"</tr>"
			"<tr><td height=\"25\" class=\"textoCeldaAzul\">%s %ld</td><td class=\"textoCeldaAzul\">%s %ld</td><td class=\"textoCeldaAzul\">%s %ld</td></tr></table>", get_moneda_por_tipo_cuenta(cuenta->tipo_cuenta), cuenta->saldo, get_moneda_por_tipo_cuenta(cuenta->tipo_cuenta), debitos, get_moneda_por_tipo_cuenta(cuenta->tipo_cuenta), creditos, id_cliente);

	bformata(html, "</center>");
	cuentas_destroy(cuenta, 1);
	return 0;
}

// funcion que atiende la URL "/movimientos_cuenta.jsp". Construye una pagina mostrando los movimientos de una cuenta de cliente.
// los movimientos de la cuenta pueden estar ordenados por fecha o por monto.
static int http_servlet_movimientos_cuenta(server_worker_job_t worker_job, http_request_t request, http_response_t response)
{
	bstring html=bfromcstr("\0");
	char *str_cuenta, *str_sort_criteria;
	char *id_cliente;
	int ret=0;

	// sin memoria disponible.
	if ((html == NULL) || (balloc(html, 32767) != BSTR_OK))
	{
		http_response_set_body(response, "<html lang=\"es\"><head/><body><h1>Error inesperado (cod: HTML_BUFFER_CREATE)</h1></body></html>", strlen("<html lang=\"es\"><head/><body><h1>Error inesperado (cod: HTML_BUFFER_CREATE)</h1></body></html>"), 0);
		http_response_set_status_code(response, 500);
		return 0;
	}

	id_cliente=http_request_get_parameter(request, "id_cliente");
	str_cuenta=http_request_get_parameter(request, "cuenta");
	str_sort_criteria=http_request_get_parameter(request, "sort");

	// construimos el html de respuesta.
	bformata(html, "<html lang=\"es\"><head><title>Movimientos de cuenta</title>");
	http_servlet_add_stylesheet(html);
	bformata(html, "</HEAD><body>");
	ret=http_servlet_get_movimientos_cuenta(worker_job, html, id_cliente, str_cuenta, str_sort_criteria);
	bformata(html, "<br/><table><tr><td class=\"textoCeldaAzul\"><a href=\"cuentas.jsp?id_cliente=%s\">Volver a cuentas</a></td></tr></table>", id_cliente);
	bformata(html, "</body></html>");
	
	// si no pudo ser resuelto el requerimiento de cuenta, lo consideramos error 500.
	if (ret != 0)
	{
		http_response_set_status_code(response, 500);
	}
	// agregamos el body a la respuesta.
	http_response_set_body(response, bdata(html), blength(html), 1);
	return 0;
}


// funcion que atiende la URL "/movimientos_all_cuentas.jsp". Construye una pagina mostrando los movimientos de todas las cuentas de un cliente.
// los movimientos de cada cuenta pueden estar ordenados por fecha o por monto.
static int http_servlet_movimientos_all_cuentas(server_worker_job_t worker_job, http_request_t request, http_response_t response)
{
	bstring html=bfromcstr("\0");
	char *str_sort_criteria;
	char *id_cliente;
	cuenta *cuentas;
	int count_cuentas;
	int i=0;
	int ret=0;

	// sin memoria disponible.
	if ((html == NULL) || (balloc(html, 8191) != BSTR_OK))
	{
		http_response_set_body(response, "<html lang=\"es\"><head/><body><h1>Error inesperado (cod: HTML_BUFFER_CREATE)</h1></body></html>", strlen("<html lang=\"es\"><head/><body><h1>Error inesperado (cod: HTML_BUFFER_CREATE)</h1></body></html>"), 0);
		http_response_set_status_code(response, 500);
		return 0;
	}

	// construimos el html de respuesta.
	bformata(html, "<html lang=\"es\"><head><title>Consolidado de cuentas</title>");
	http_servlet_add_stylesheet(html);
	bformata(html, "</HEAD><body>");


	id_cliente=http_request_get_parameter(request, "id_cliente");
	// el id de cliente no fue especificado.
	if (id_cliente == NULL)
	{
		bformata(html, "<h1>No fue especificado el ID de cliente</h1></body></html>");
		http_response_set_status_code(response, 500);
		return 0;
	}
	str_sort_criteria=http_request_get_parameter(request, "sort");

	// buscamos todas las cuentas del cliente.
	cuentas=http_servlet_get_cuentas_cliente(worker_job, html, id_cliente, &count_cuentas);
	if (cuentas == NULL)
	{
		bformata(html, "</body></html>");
		http_response_set_body(response, bdata(html), blength(html), 1);
		http_response_set_status_code(response, 500);
		return 0;
	}

	// para cada cuenta, ejecutamos la consulta de movimientos.
	for (i=0; i<count_cuentas; i++)
	{
		ret=http_servlet_get_movimientos_cuenta(worker_job, html, id_cliente, cuentas[i].cuenta, str_sort_criteria);
		if (ret != 0)
		{
			cuentas_destroy(cuentas, count_cuentas);
			// si no pudo ser resuelto el requerimiento de cuenta, lo consideramos error 500 y no continuamos con el resto de las cuentas.
			bformata(html, "</body></html>");
			http_response_set_body(response, bdata(html), blength(html), 1);

			http_response_set_status_code(response, 500);
			return 0;
		}
	}
	bformata(html, "<br/><table><tr><td class=\"textoCeldaAzul\"><a href=\"cuentas.jsp?id_cliente=%s\">Volver a cuentas</a></td></tr></table>", id_cliente);

	// Si se trata de un cliente particular agregamos relleno (Loren Ipsum).
	if (strcmp(id_cliente,"25") == 0)
	{
		bformata(html,lorem_ipsum);

	}
	bformata(html, "</body></html>");

	cuentas_destroy(cuentas, count_cuentas);
	
	// agregamos el body a la respuesta.
	http_response_set_body(response, bdata(html), blength(html), 1);
	return 0;
}

// funcion que atiende la URL "/load_balancer.jsp". Deriva un requerimiento en 4 posibles servidores proveedores de informacion.
static int http_servlet_load_balancer(server_worker_job_t worker_job, http_request_t request, http_response_t response)
{
	http_request_t newrequest = NULL;
	http_response_t newresponse = NULL;
	int response_status_code=200;
	char *resp_buffer;
	int resp_buffer_len;


	newrequest=http_servlet_create_request(worker_job, getenv("TESIS_ACCOUNTS_IP_SERVICE"),get_random_port_load_balancer(),"/service/movimientos_cuenta.jsp", (enum http_method)HTTP_GET,1,0);
	if (newrequest == NULL)
	{
		http_response_set_status_code(response, 500);
		return 0;
	}

	// agregado de parametro de id cliente al subrequest.
	http_request_add_parameter(newrequest, "cuenta", "35001");

	// pedimos las cuentas del cliente.
	newresponse=http_servlet_send_request_and_create_response(newrequest, worker_job);
	if (newresponse == NULL)
	{
		http_servlet_destroy_request(newrequest);
		http_servlet_destroy_response(newresponse);
		logger_app_error("%s","[http_servlet_load_balancer] error doing load balancer.");
		http_response_set_status_code(response, 500);
		return 0;
	}
	
	response_status_code=http_response_get_status_code(newresponse);
	if (response_status_code != 200)
	{
		http_servlet_destroy_request(newrequest);
		http_servlet_destroy_response(newresponse);
		logger_app_error("%s","[http_servlet_load_balancer] error doing load balancer2.");
		http_response_set_status_code(response, 501);
		return 0;
	}
	http_response_get_body(newresponse, (const char **)&(resp_buffer), &(resp_buffer_len));

	// agregamos el body a la respuesta. Hacemos una copia del obtenido en la respuesta.
	http_response_set_body(response, resp_buffer, resp_buffer_len, 0);
	http_response_set_status_code(response, 200);

	http_servlet_destroy_request(newrequest);
	http_servlet_destroy_response(newresponse);
	return 0;
}
// funcion que atiende la URL "/dato2.html". Deriva el requerimiento a nginx
static int http_servlet_simple(server_worker_job_t worker_job, http_request_t request, http_response_t response)
{
	http_request_t newrequest = NULL;
	http_response_t newresponse = NULL;
	int response_status_code=200;
	char *resp_buffer;
	int resp_buffer_len;


	newrequest=http_servlet_create_request(worker_job, getenv("TESIS_ACCOUNTS_IP_SERVICE"),80,"/dato2.html", (enum http_method)HTTP_GET,1,0);
	if (newrequest == NULL)
	{
		http_response_set_status_code(response, 500);
		return 0;
	}

	newresponse=http_servlet_send_request_and_create_response(newrequest, worker_job);
	if (newresponse == NULL)
	{
		http_servlet_destroy_request(newrequest);
		http_servlet_destroy_response(newresponse);
		logger_app_error("%s","[http_servlet_load_balancer] error doing dato2.");
		http_response_set_status_code(response, 500);
		return 0;
	}
	
	response_status_code=http_response_get_status_code(newresponse);
	if (response_status_code != 200)
	{
		http_servlet_destroy_request(newrequest);
		http_servlet_destroy_response(newresponse);
		logger_app_error("%s","[http_servlet_load_balancer] error doing dato2.");
		http_response_set_status_code(response, 501);
		return 0;
	}
	http_response_get_body(newresponse, (const char **)&(resp_buffer), &(resp_buffer_len));

	// agregamos el body a la respuesta. Hacemos una copia del obtenido en la respuesta.
	http_response_set_body(response, resp_buffer, resp_buffer_len, 0);
	http_response_set_status_code(response, 200);

	http_servlet_destroy_request(newrequest);
	http_servlet_destroy_response(newresponse);
	return 0;
}

static int http_servlet_timeout(server_worker_job_t worker_job, http_request_t request, http_response_t response)
{
	socket_t socket = NULL;

	char *hostname;
	int port = 0;
	int error=0;
	char buf[1024];
	char *timeout_str;


	//logger_app_error("[http_servlet_timeout] user_data: %p", worker_get_user_data(NULL));
	
	hostname=http_request_get_parameter(request,"host");
	port=atoi(http_request_get_parameter(request,"port"));

	socket=socket_tcp_create_and_connect(worker_job, hostname, port, &error);
	if (socket == NULL)
	{
		return 1;
	}

	timeout_str=http_request_get_parameter(request, "timeout");

	socket_set_read_timeout(socket,atoi(timeout_str));
	strcpy(buf,"GET / HTTP/1.0\r\nHost:");
	strcat(buf,hostname);
	strcat(buf,"\r\nConnection:close\r\n\r\n");

	logger_app_error("[http_servlet_timeout] REQ: %s", buf);

	socket_write(socket,buf,strlen(buf));
	error=socket_read(socket, buf,100);

	logger_app_error("[http_servlet_timeout] read devuelve %d", error);

	socket_close(socket);

	if (error > 0)
	{
		http_response_set_body(response, buf, error, 0);
	}
	else
	{
		http_response_set_body(response, "ABC", 3, 0);
	}
	http_response_set_status_code(response, 200);

	return 0;
}

static int http_servlet_rendezvous_child(server_worker_job_t worker_job, void *args)
{
	//socket_t socket = NULL;

	//char *hostname;
	//int port = 0;
	//int error=0;
	//char buf[1024];

	/**
	hostname="www.google.com.ar";
	port=80;

	socket=socket_tcp_create_and_connect(worker_job, hostname, port, &error);
	if (socket == NULL)
	{
		return 1;
	}

	strcpy(buf,"GET / HTTP/1.0\r\nHost:");
	strcat(buf,hostname);
	strcat(buf,"\r\nConnection:close\r\n\r\n");

	logger_app_error("[http_servlet_rendezvous_child] REQ: %s", buf);

	socket_write(socket,buf,strlen(buf));
	error=socket_read(socket, buf,100);

	logger_app_error("[http_servlet_rendezvous_child] read devuelve %d", error);

	socket_close(socket);
	
	if (error > 0)
	{
		memcpy(args, (const void *)(buf), error);
	}
	**/
	/*for (port=0;port<99000000;port++)
	{
		buf[0]='\0';
	}*/
	memcpy(args, "ABC",3);
	return 0;
}

static int http_servlet_rendezvous(server_worker_job_t worker_job, http_request_t request, http_response_t response)
{
	server_worker_job_t child;
	char buf[1024];
	server_worker_job_children children;
	int ret=0;
	
	memset(buf,0,1024);
	
	logger_app_error("%s","[http_servlet_rendezvous] previo lanzar child");
	child = server_worker_job_prepare_priority(worker_job, http_servlet_rendezvous_child, (void *)buf, MASHUP_SERVER_PRIORITY_QUEUE_HIGH);
	logger_app_error("%s","[http_servlet_rendezvous] child lanzado");
	
	
	// espero la finalizacion del hijo.
	children.count=1;
	children.child[0]=child;
	
	ret =server_worker_job_wait_children(worker_job, &(children));
	
	logger_app_error("[http_servlet_rendezvous] wait children devuelve %d",ret);
	
	if (ret == 0)
	{
		logger_app_error("[http_servlet_rendezvous] children cargo %s",buf);	
	}	
	return 0;	
}

int http_servlet_init()
{
	int ret=0;
	request_path_mapping = hashmap_create(3);
	ret=hashmap_put_ref_data(request_path_mapping,"/cuentas.jsp", http_servlet_cuentas);
	ret=hashmap_put_ref_data(request_path_mapping,"/movimientos_cuenta.jsp", http_servlet_movimientos_cuenta);
	ret=hashmap_put_ref_data(request_path_mapping,"/movimientos_all_cuentas.jsp", http_servlet_movimientos_all_cuentas);
	ret=hashmap_put_ref_data(request_path_mapping,"/load_balancer.jsp", http_servlet_load_balancer);
	ret=hashmap_put_ref_data(request_path_mapping,"/dato2.html", http_servlet_simple);

	ret=hashmap_put_ref_data(request_path_mapping,"/dato3.html", http_servlet_timeout);
	
	ret=hashmap_put_ref_data(request_path_mapping,"/rendezvous.html", http_servlet_rendezvous);

	return (ret || http_init());
}
